package com.cdg.polymeric;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;

/**
 * 反射工具类
 * @author wyp
 * @version 2021-06-04
 */
public class ReflectUtils {


    /**
     * 反射调用目标方法
     * @param target
     * @param methodName
     * @param objects
     * @return
     */
    public static Object invokeMethod(final Object target,final String methodName,final Object... objects) throws InvocationTargetException, IllegalAccessException {

        if(target == null){
            return null;
        }

        Class<?> targetType = target.getClass();
        Method invokeMethod = null;
        Method[] ms = targetType.getMethods();
        for(Method m:ms){
            // 方法名称不匹配忽略
            if(!m.getName().equals(methodName)){
                continue;
            }
            // 如果参数类型长度不匹配 忽略
            Class<?>[] paramTypes = m.getParameterTypes();
            if(paramTypes.length != objects.length){
                continue;
            }

            //3.如果有参数类型不一样的也忽略---1
            boolean isFind = true;
            for (int i = 0; i < paramTypes.length; i++) {
                Object param_object = objects[i];
                if (param_object == null) {
                    continue;
                }
                //
                if (!paramTypes[i].isAssignableFrom(param_object.getClass())) {
                    isFind = false;
                    break;
                }
            }
            //5.如果有参数类型不一样的也忽略---2
            if (!isFind) {
                continue;
            }
            //符合条件执行调用
            invokeMethod = m;
        }
        if (invokeMethod == null) {
            throw new NullPointerException(methodName + " invokeMethod is null.");
        } else {
            return invokeMethod.invoke(target, objects);
        }

    }


    /**
     * 获取类定义的字段和继承父类中定义的字段以及父类的父类（子类重新定义同名字段也会被列入集合）
     * @param target
     * @return
     */
    public static List<Field> findAllFields(final Class<?> target){

        if (target == null) {
            return null;
        }
        ArrayList<Field> fList = new ArrayList<>();
        findALLFields(target, fList);
        return fList;

    }


    private static void findALLFields(final Class<?> target, final ArrayList<Field> fList) {
        if (target == null) {
            return;
        }
        for (Field field : target.getDeclaredFields()) {
            if (!fList.contains(field)) {
                fList.add(field);
            }
        }
        for (Field field : target.getFields()) {
            if (!fList.contains(field)) {
                fList.add(field);
            }
        }
        Class<?> superType = target.getSuperclass();
        if (superType == null || superType == target) {
            return;
        }
        findALLFields(superType, fList);
    }

    /**
     * 获取定义的方法和继承父类中定义的方法以及父类的父类
     * @param target
     * @return
     */
    public static List<Method> findAllMethods(final Class<?> target){
        if(target == null){
            return null;
        }

        ArrayList<Method> mList = new ArrayList<>();
        findAllMethods(target,mList);
        return mList;
    }

    private static void findAllMethods(final Class<?> target,final ArrayList<Method> mList){
        if(target == null){
            return;
        }

        for(Method method : target.getDeclaredMethods()){
            if(!mList.contains(method)){
                mList.add(method);
            }
        }

        for(Method method : target.getMethods()){
            if(!mList.contains(method)){
                mList.add(method);
            }
        }

        Class<?> superType = target.getSuperclass();
        if(superType == null || superType == target){
            return;
        }

        findAllMethods(superType,mList);
    }


    /**
     * 查找一个可以操作的字段列表
     * @param type
     * @return
     */
    public static List<Field> getFields(final Class<?> type){
        return Arrays.asList(type.getFields());
    }

    /**
     * 查找一个可以操作的方法列表
     * @param type
     * @return
     */
    public static List<Method> getMethods(final Class<?> type){
        return Arrays.asList(type.getMethods());
    }


    /**
     * 查找一个可以操作的字段
     * @param fieldName
     * @param type
     * @return
     */
    public static Field getField(final String fieldName,Class<?> type){
        if(fieldName==null || type == null){
            return null;
        }
        for(Field field : type.getDeclaredFields()){
            if(field.getName().equals(fieldName)){
                return field;
            }
        }

        for(Field field : type.getFields()){
            if(field.getName().equals(fieldName)){
                return field;
            }
        }

        return null;
    }

    /**
     * 获取需要在反射中映射对象的字段值
     * @param type
     * @param param
     * @return
     */
    public static Map<String, Object> getFieldValue(Class<?> type, Object param){

    Map<String,Object> fieldValueMap = new HashMap<>();
    Class clazz = type;
    List<Field> allFields = new ArrayList<>(100);
	allFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
    Class clazzSuper = clazz.getSuperclass();
		while (clazzSuper != Object.class) {
        allFields.addAll(Arrays.asList(clazzSuper.getDeclaredFields()));
        clazzSuper = clazzSuper.getSuperclass();
    }

	allFields.stream().forEach(field -> {

        field.setAccessible(true);
            try {
                String fieldName = field.getName();
                Object fieldVal = field.get(param);
                fieldValueMap.put(fieldName,fieldVal);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    });

		return fieldValueMap;
    }

    /**
     * 设置字段属性值
     * @param obj
     * @param property
     * @param value
     * @return
     */
    public static Object setObjectProperties(Object obj,String property,Object value){
        if (obj != null){
            Class type = obj.getClass();
            try {
                Field field = type.getDeclaredField(property);
                field.setAccessible(true);
                field.set(obj,value);
                return obj;
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }


        return obj;
    }




}
